package com.waldamartin.android.mobileapplicationspairsapp.control;


import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Locale;
import java.util.Random;

import com.waldamartin.android.mobileapplicationspairsapp.R;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Typeface;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.GridLayout;
import android.widget.GridLayout.LayoutParams;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;


/**
 * Class to deal with the user input of the current game and
 * to provide the game logic.
 * 
 * @author Martin
 */
public class GameActivity extends Activity
{
	// Temporarily data model
	private ArrayList<Integer> set;
	
	// GUI
	private TextView[] tvPlayers;
	private ImageView ivPlayer1TurnAdvice1;
	private ImageView ivPlayer1TurnAdvice2;
	private ImageView ivPlayer2TurnAdvice1;
	private ImageView ivPlayer2TurnAdvice2;
	private GridLayout glGameboard;
	private Animation fadeOutAnimation;
	private Animation fadeInAnimation;
	
	// Logic
	// Logic to deal with cards
	private Integer uncoveredImageRes;
	private ImageView uncoveredImageView;
	private Integer amountOfUncoveredImages;
	
	// Logic to deal which turn it is
	private int currentPlayer;
	private int[] playersScore;
	private String gameName;
	
	// Geo
	private Double longitudeGps;
	private Double latitudeGps;
	private Double longitudeNetwork;
	private Double latitudeNetwork;
	private LocationManager locationManager;
	private LocationListener locationListenerGps;
	private LocationListener locationListenerNetwork;
	
	// Constants
	private static final int AMOUNT_OF_PLAYERS = 2; 
	private static final int MAX_UNCOVERED_CARDS = 2;
	private static final int PLAYER_ONE = 0;
	private static final int PLAYER_TWO = 1;
	
	@Override
	protected void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_game);
		
		// Initialization
		
		// Initialize location service
		locationListenerGps = new GameLocationListener();
		locationListenerNetwork = new GameLocationListener();
		locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
		
		// Add location listener for GPS and NETWORK
		locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1, 0, locationListenerGps);
		locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 1, 0, locationListenerNetwork); 
		
		// Change title bar to game name
		gameName = getIntent().getStringExtra("name");
		
		// Set player to start game randomly
		Random random = new Random();
		currentPlayer = random.nextInt(AMOUNT_OF_PLAYERS); // Random values between 0 and 1
		
		// Create the right amount of players to memorise score
		playersScore = new int[AMOUNT_OF_PLAYERS];
			
		// All cards remain subsurface on start
		amountOfUncoveredImages = 0;

		// Initialize animations
		fadeOutAnimation = AnimationUtils.loadAnimation(this, R.animator.fade_out_animation);
		fadeInAnimation = AnimationUtils.loadAnimation(this, R.animator.fade_in_animation);
		
		// Connect to GUI and initialize
		glGameboard = (GridLayout) findViewById(R.id.glGameboard);
		tvPlayers = new TextView[AMOUNT_OF_PLAYERS];
		tvPlayers[PLAYER_ONE] = (TextView) findViewById(R.id.tvPlayer1);
		tvPlayers[PLAYER_ONE].setText(getResources().getString(R.string.player) + " " + (PLAYER_ONE+1));
		tvPlayers[PLAYER_TWO] = (TextView) findViewById(R.id.tvPlayer2);
		tvPlayers[PLAYER_TWO].setText(getResources().getString(R.string.player) + " " + (PLAYER_TWO+1));
		ivPlayer1TurnAdvice1 = (ImageView) findViewById(R.id.ivHint11);
		ivPlayer1TurnAdvice2 = (ImageView) findViewById(R.id.ivHint12);
		ivPlayer2TurnAdvice1 = (ImageView) findViewById(R.id.ivHint21);
		ivPlayer2TurnAdvice2 = (ImageView) findViewById(R.id.ivHint22);
		
		// Preset turn advice depending on starting player
		if (currentPlayer == PLAYER_ONE)
		{
			ivPlayer2TurnAdvice1.setVisibility(ImageView.INVISIBLE);
			ivPlayer2TurnAdvice2.setVisibility(ImageView.INVISIBLE);
		}
		else
		{	
			ivPlayer1TurnAdvice1.setVisibility(ImageView.INVISIBLE);
			ivPlayer1TurnAdvice2.setVisibility(ImageView.INVISIBLE);
		}
		
		// Highlight player advice of starting player
		tvPlayers[currentPlayer].setTypeface(null, Typeface.BOLD);
		
		// Listen to know about the available game board size
		glGameboard.getViewTreeObserver().addOnGlobalLayoutListener(new OnGameboardGlobalLayoutListener() );
		
		// Add all cards twice to get pairs
		// Add set first time
		set = getIntent().getIntegerArrayListExtra("set");
		int oldSetSize = set.size();
		// Add set second time
		for(int i = 0; i < oldSetSize; i++)
		{
			set.add(set.get(i));
		}
		
		// Shuffle cards to finish game setup
		Collections.shuffle(set);
	}
	
	/**
	 * Class to handle location request.
	 * 
	 * @author Martin
	 */
	private class GameLocationListener implements LocationListener
	{
		@Override
		public void onLocationChanged(Location location)
		{
			// Differ between location providers
			if (location.getProvider().equals(LocationManager.GPS_PROVIDER))
			{
				longitudeGps = location.getLongitude();
				latitudeGps = location.getLatitude();
				
				// We need the location just once
				locationManager.removeUpdates(locationListenerGps);
				locationManager.removeUpdates(locationListenerNetwork);
				locationListenerGps = null;
				locationListenerNetwork = null;				
				
				// Debug
				System.out.println("GPS-longitude: " + longitudeGps + "   GPS-latitude: " + latitudeGps);
			}
			else
			{
				longitudeNetwork = location.getLongitude();
				latitudeNetwork = location.getLatitude();
				
				// We need the location just once.. but maybe we get a more precise coordinate later by GPS
				locationManager.removeUpdates(locationListenerNetwork);
				locationListenerNetwork = null;
				
				// Debug
				System.out.println("NET-longitude: " + longitudeNetwork + "   NET-latitude: " + latitudeNetwork);
			}
			
		}

		@Override
		public void onStatusChanged(String provider, int status, Bundle extras)
		{ 
			// Nothing to do..
		}

		@Override
		public void onProviderEnabled(String provider)
		{
			// Nothing to do..
		}

		@Override
		public void onProviderDisabled(String provider)
		{
			// Nothing to do..
		}
	}

	@Override
	protected void onPause()
	{
		super.onPause();
		
		// Remove listener at least at this point
		if (locationListenerGps != null)
		{
			locationManager.removeUpdates(locationListenerGps);
			locationListenerGps = null;
		}
		
		if (locationListenerNetwork != null)
		{
			locationManager.removeUpdates(locationListenerNetwork);
			locationListenerNetwork = null;
		}
	}
	
	
	
	/**
	 * Class to create a dialog to display the game result message later on.
	 * 
	 * @author Martin
	 */
	private class WinnerDialogBuilder extends AlertDialog.Builder
	{	
		public WinnerDialogBuilder(Context context, String message)
		{
			super(context);
			
			// Configure dialog
			this.setMessage(message);
			this.setCancelable(false);
			this.setPositiveButton(R.string.ok, new OnConfirmClickListener());
		}
		
		/**
		 * Inner class to handle action on confirmation click
		 * 
		 * @author Martin
		 */
		private class OnConfirmClickListener implements DialogInterface.OnClickListener
		{
			@Override
			public void onClick(DialogInterface dialog, int which)
			{
				// On user confirmation: Intent to go back to GameView
				Intent back = new Intent(GameActivity.this, GameListActivity.class);
				
				// Formatting for date to string
				SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.ENGLISH);
				String date = sdf.format(new Date());
				
				
				// Append data to intent
				back.putExtra("date", date);
				
				if ( (latitudeGps != null) && (longitudeGps != null) )
				{
					back.putExtra("latitude", latitudeGps);
					back.putExtra("longitude", longitudeGps);
				}
				else if ( (latitudeNetwork != null) && (longitudeNetwork != null) )
				{
					back.putExtra("latitude", latitudeNetwork);
					back.putExtra("longitude", longitudeNetwork);
				}
					
				// Prevent GamesView to go back to finished game (pop this activity from stack)
				back.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
				
				startActivity(back);
			}
		} // OnConfirmClickListener End
		
	} // WinnerDialogBuilder End
	
	
	/**
	 * Show the result dialog for the current game.
	 * 
	 * @param message The message that is shown in the game result dialog
	 */
	private void showWinnderDialog(String message)
	{	
		// Create and show dialog
		final WinnerDialogBuilder winnderDialogBuilder = new WinnerDialogBuilder(this, message);
		final AlertDialog alert = winnderDialogBuilder.create( );
		alert.show( );
	}
		
	
	/**
	 * Class to deal with gameboard setup
	 * 
	 * @author Martin
	 */
	private class OnGameboardGlobalLayoutListener implements OnGlobalLayoutListener
	{
		@Override
		public void onGlobalLayout()
		{			
			int yDim;
			int xDim;
			
			// Math to calculate columns and rows
			double squareRoot = Math.sqrt(set.size());
			double decimalPlace = squareRoot - Math.floor(squareRoot);
	
			if (decimalPlace < 0.5 && decimalPlace != 0)
			{
				// An additional row is needed
				xDim = (int)Math.floor(squareRoot);
				yDim = xDim+1;
			}
			else
			{
				// Same amount
				xDim = (int)Math.ceil(squareRoot);
				yDim = xDim;
			}
			
			// Prepare gameboard structure
			glGameboard.setColumnCount(xDim);
			glGameboard.setRowCount(yDim);
			
		
			// Retrieve the width and height
			int viewWidth = glGameboard.getWidth();
			int viewHeight = glGameboard.getHeight();
			
			int imageSize;
			if (viewWidth < viewHeight)
			{
				imageSize = viewWidth / xDim;
			}
			else
			{
				imageSize = viewHeight / yDim;
			}
			
			// After size calculation: Add cards on gameboard
			int counter = 0; // to go through card deck
			for (int rows = 0; rows < yDim; rows++)
			{
				// Add cards column by column
				for (int columns = 0; (columns < xDim) && (counter < set.size()); columns++)
				{
					ImageView iv = new ImageView(GameActivity.this);
					iv.setImageResource(R.drawable.ic_back); // Initialization face down
					
					// Random rotation for positioning (more realistic)
					Random random = new Random();
					int x = random.nextInt(4); // Random values between 0 and 3
					iv.setRotation(x * 90);
					
					// Use calculated size
					LayoutParams params = new LayoutParams();
					params.height = imageSize;
					params.width = imageSize;
					
					// Add card to gameboard
					glGameboard.addView(iv, params);
					
					// Add listener for user interaction
					iv.setOnClickListener(new CardOnClickListener(set.get(counter)));
					
					// next card deck card
					counter++;
				}
			}
			
			// Center gameboard between the two players
			RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
			params.addRule(RelativeLayout.CENTER_VERTICAL);
			glGameboard.setLayoutParams(params);			
			
			// Get rid of listener
			glGameboard.getViewTreeObserver().removeOnGlobalLayoutListener(this);
		} // onGlobalLayout end
	} // OnGameboardGlobalLayoutListener end
	
	/**
	 * Class to handle user interaction.
	 * 
	 * @author Martin
	 */
	private class CardOnClickListener implements OnClickListener
	{
		private Integer refImage;	   
		
		public CardOnClickListener(Integer refImage)
		{
			// Remember own card motive
			this.refImage = refImage;
		}
		
		@Override
		public void onClick(View view)
		{
			if (amountOfUncoveredImages >= MAX_UNCOVERED_CARDS)
			{
				// Prevent to uncover more than two images at a time
				return;
			}
			
			// Cast to change image resource
			ImageView tempImageView = (ImageView) view;
			
			if (uncoveredImageView == tempImageView)
			{
				// Prevent to select the same card twice 
				return;
			}
			
			// Uncover selected card
			tempImageView.setImageResource(refImage);
			view = tempImageView;
			
			amountOfUncoveredImages++;
				
			if (uncoveredImageRes == null)
			{
				// First of two images uncovered.. remember!
				uncoveredImageRes = refImage;
				uncoveredImageView = tempImageView;

				return;
			}

			// Cover both uncovered cards after specified period of time
			Handler handler = new Handler();
			handler.postDelayed(new CoverRunnable(tempImageView), 1000); 
		}

		/**
		 * Class to cover two cards after a period of time
		 * 
		 * @author Martin
		 */
		private class CoverRunnable implements Runnable
		{
			private ImageView tempImageView;
			
			public CoverRunnable(ImageView tempImageView)
			{
				this.tempImageView = tempImageView;
			}
			
			@Override
			public void run()
			{
				if (uncoveredImageRes.equals(refImage))
				{
					// Found a pair
					
					// Take both cards out
					tempImageView.setVisibility(View.INVISIBLE);
					uncoveredImageView.setVisibility(View.INVISIBLE);
					
					// Reward player
					playersScore[currentPlayer]++;
					tvPlayers[currentPlayer].setText(getResources().getString(R.string.player) + " " + 
													 (currentPlayer+1) + ": " + playersScore[currentPlayer] + 
													 " " + getResources().getString(R.string.points));
					
					// Check for winner
					if ( ((playersScore[0] + playersScore[1])*AMOUNT_OF_PLAYERS) == (set.size()) )
					{
						String winner = "Nobody";
						if (playersScore[0] > playersScore[1])
						{
							winner = getResources().getString(R.string.player) + " 1";
						}
						else if (playersScore[0] < playersScore[1])
						{
							winner = getResources().getString(R.string.player) + " 2";
						}
						
						winner += " " + getResources().getString(R.string.wins_the) + 
								  " " + gameName + " " + getResources().getString(R.string.game); 
						
						// Show game result dialog
						showWinnderDialog(winner);
					}
	
				}
				else
				{
					// No pair found
					
					// Cover both again
					uncoveredImageView.setImageResource(R.drawable.ic_back);
					tempImageView.setImageResource(R.drawable.ic_back);
					
					tvPlayers[currentPlayer].setTypeface(null, Typeface.NORMAL);
					
					// Now it is next players turn
					currentPlayer = (currentPlayer + 1) % AMOUNT_OF_PLAYERS;
					
					// Show which players turn it is
					tvPlayers[currentPlayer].setTypeface(null, Typeface.BOLD);
					if (currentPlayer == 1)
					{
						// Fade in Player 1
						ivPlayer1TurnAdvice1.startAnimation(fadeOutAnimation);
						ivPlayer1TurnAdvice1.setVisibility(ImageView.INVISIBLE);
						ivPlayer1TurnAdvice2.startAnimation(fadeOutAnimation);
						ivPlayer1TurnAdvice2.setVisibility(ImageView.INVISIBLE);
						
						ivPlayer2TurnAdvice1.startAnimation(fadeInAnimation);
						ivPlayer2TurnAdvice1.setVisibility(ImageView.VISIBLE);
						ivPlayer2TurnAdvice2.startAnimation(fadeInAnimation);
						ivPlayer2TurnAdvice2.setVisibility(ImageView.VISIBLE);
					}
					else
					{
						// Fade in Player 2
						ivPlayer1TurnAdvice1.startAnimation(fadeInAnimation);
						ivPlayer1TurnAdvice1.setVisibility(ImageView.VISIBLE);
						ivPlayer1TurnAdvice2.startAnimation(fadeInAnimation);
						ivPlayer1TurnAdvice2.setVisibility(ImageView.VISIBLE);
						
						ivPlayer2TurnAdvice1.startAnimation(fadeOutAnimation);
						ivPlayer2TurnAdvice1.setVisibility(ImageView.INVISIBLE);
						ivPlayer2TurnAdvice2.startAnimation(fadeOutAnimation);
						ivPlayer2TurnAdvice2.setVisibility(ImageView.INVISIBLE);
					}
				}

				// Reset
				uncoveredImageRes = null;
				uncoveredImageView = null;
				amountOfUncoveredImages = 0;
			}
		} // CoverRunnable End

	} // CardOnClickListener End
	
}